package com.bazaaroid.server.gae.core.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.bazaaroid.server.gae.core.dao.GenericDAO;
import com.bazaaroid.server.gae.core.persistence.PMF;
import com.google.appengine.api.datastore.Key;
import com.google.inject.Inject;

public abstract class JDOGenericDAOImpl<T, ID extends Serializable>
		implements GenericDAO<T, ID> {

	@Inject
	private PMF pmf;
	private PersistenceManager persistenceManager;

	private Class<T> entityClass;

	@SuppressWarnings("unchecked")
	public JDOGenericDAOImpl() {
		this.entityClass = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public PersistenceManager getPersistenceManager() {
		return pmf.getPersistenceManager();
	}

	public T getById(ID id) {
		persistenceManager = pmf.getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);

		T entity = null;

		try {
			entity = persistenceManager.getObjectById(getEntityClass(), id);
		} finally {
			persistenceManager.close();
		}

		return entity;
	}

	@SuppressWarnings("unchecked")
	public List<T> getByKeys(Collection<Key> keys) {
		persistenceManager = pmf.getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);

		Query query = persistenceManager.newQuery(getEntityClass());
	    query.declareImports("import java.util.Collection;");
		query.setFilter("paramKeys.contains(key)");
		query.declareParameters("Collection paramKeys");

		List<T> entities = new ArrayList<T>();

		try {
			entities = (List<T>) query.execute(keys);
			entities.size();
		}
		finally {
			query.closeAll();
			persistenceManager.close();
		}

		return entities;
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		persistenceManager = pmf.getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);

		Query query = persistenceManager.newQuery(getEntityClass());
		List<T> entities = null;

		try{
			entities = (List<T>) query.execute();
			entities.size();
		}
		finally {
			query.closeAll();
			persistenceManager.close();
		}

		return entities;
	}

	@SuppressWarnings("unchecked")
	public List<T> getByQuery(String filter, String declareParams,
			Object... params) {

		persistenceManager = pmf.getPersistenceManager();
		List<T> entities = null;

		try{
			Query query = persistenceManager.newQuery(getEntityClass());

			if(filter != null)
				query.setFilter(filter);

			if(params != null) {
				query.declareParameters(declareParams);
				entities = (List<T>) query.executeWithArray(params);
			}
			else
				entities = (List<T>) query.execute();

			entities = (List<T>) persistenceManager.detachCopyAll(entities);
		}
		catch(JDOException e) {
			e.printStackTrace();
		}
		finally {
			persistenceManager.close();
		}

		return entities;

	}

	public T makePersistent(T entity) {
		persistenceManager = pmf.getPersistenceManager();
		T persistentEntity = null;
		
		try {
			persistentEntity = persistenceManager.makePersistent(entity);
        } finally {
        	persistenceManager.close();
        }
        
		return persistentEntity;
	}

	public void deletePersistent(T entity) {
		persistenceManager = pmf.getPersistenceManager();
		
		try {
			persistenceManager.deletePersistent(entity);
        } finally {
        	persistenceManager.close();
        }
	}

	public void deleteAll(Collection<Key> keys) {
		persistenceManager = pmf.getPersistenceManager();

		Query query = persistenceManager.newQuery(getEntityClass());
		query.declareImports("import java.util.Collection;");
		query.setFilter("paramKeys.contains(key)");
		query.declareParameters("Collection paramKeys");

		try {
			query.deletePersistentAll(keys);
        } finally {
        	query.closeAll();
        	persistenceManager.close();
        }
	}

	public void flush() {
		persistenceManager = pmf.getPersistenceManager();
	
		try {
			persistenceManager.flush();
		} finally {
			persistenceManager.close();
		}	
	}

}